package se.jocke.nb.proxy.monitor.service.impl;

import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.QueryStringDecoder;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.littleshoot.proxy.HttpFiltersAdapter;
import se.jocke.nb.proxy.monitor.model.HttpMessage;
import se.jocke.nb.proxy.monitor.model.MessageState;
import se.jocke.nb.proxy.monitor.service.MessageListener;

/**
 *
 * @author jocke
 */
public class HttpFiltersImpl extends HttpFiltersAdapter {

    private final MessageListener messageListener;

    private final HttpMessage message;

    private static final Logger LOG = Logger.getLogger(HttpFiltersImpl.class.getName());

    public HttpFiltersImpl(HttpRequest originalRequest, MessageListener listener) {
        super(originalRequest);
        this.messageListener = listener;
        this.message = new HttpMessage(UUID.randomUUID().toString());
    }

    @Override
    public HttpResponse requestPre(HttpObject httpObject) {
        LOG.log(Level.INFO, httpObject.getClass().getName());

        LOG.info(httpObject.getClass().getName());

        message.setState(MessageState.REQUEST);

        if (httpObject instanceof HttpRequest) {
            setRequestValues(httpObject, message);
        }

        messageListener.onMessage(message);

        return super.requestPost(httpObject);
    }

    @Override
    public HttpObject responsePost(HttpObject httpObject) {
        LOG.log(Level.INFO, httpObject.getClass().getName());

        if (httpObject instanceof HttpResponse) {
            HttpResponse hr = (HttpResponse) httpObject;
            message.setState(MessageState.RESPONSE);
            message.setResponseHeaders(headersToMap(hr.headers()));
            message.setStatusCode(hr.getStatus().code());
            message.setReasonPhrase(hr.getStatus().reasonPhrase());
        }

        if (httpObject instanceof DefaultFullHttpResponse) {
            DefaultFullHttpResponse content = (DefaultFullHttpResponse) httpObject;
            message.setContent(getData(content.content()));
        }

        messageListener.onMessage(message);

        return super.responsePost(httpObject);
    }

    @Override
    public HttpObject responsePre(HttpObject httpObject) {
        return responsePost(httpObject);
    }

    @Override
    public HttpResponse requestPost(HttpObject httpObject) {
        if (httpObject instanceof FullHttpRequest) {
            FullHttpRequest fhr = (FullHttpRequest) httpObject;
            message.setBody(getData(fhr.content()));
        }
        return super.requestPost(httpObject);
    }

    private Map<String, String> headersToMap(HttpHeaders headers) {
        if (headers.entries().isEmpty()) {
            return Collections.EMPTY_MAP;

        } else {
            Map<String, String> map = new HashMap<String, String>(headers.entries().size());
            List<Map.Entry<String, String>> entries = headers.entries();
            for (Map.Entry<String, String> entry : entries) {
                map.put(entry.getKey(), entry.getValue());
            }
            return map;
        }
    }

    private void setRequestValues(HttpObject httpObject, HttpMessage message) {
        DefaultHttpRequest hr = (DefaultHttpRequest) httpObject;
        QueryStringDecoder decoder = new QueryStringDecoder(hr.getUri());
        message.setPath(decoder.path());
        message.setMethod(hr.getMethod().name());
        message.setRequestHeaders(headersToMap(hr.headers()));
        message.setRequestParameters(decoder.parameters());
    }

    private byte[] getData(ByteBuf content) {
        if (content.isReadable()) {
            ByteBuffer nioBuffer = content.nioBuffer();
            byte[] b = new byte[nioBuffer.remaining()];
            nioBuffer.get(b);
            return b;
        } else {
            return null;
        }
    }
}
